/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

// 协议类型常量定义
const bit<16> TUNNEL_TYPE       = 0x1212;
const bit<16> IPV4_TYPE         = 0x800;
const bit<16> ARP_TYPE          = 0x0806;
const bit<32> MAX_TUNNEL_ID     = 1 << 16;

// ARP协议常量
const bit<16> ARP_ETHERNET_HW   = 0x0001;
const bit<16> ARP_IPV4_PROTO    = 0x0800;
const bit<8>  ARP_ETHERNET_LEN  = 6;
const bit<8>  ARP_IPV4_LEN      = 4;
const bit<16> ARP_REQUEST       = 1;
const bit<16> ARP_REPLY         = 2;

/*************************************************************************
*********************** 数据类型与头部定义 ******************************
*************************************************************************/

typedef bit<9>  PortId;         // 端口标识类型
typedef bit<48> MacAddress;     // MAC地址类型
typedef bit<32> Ip4Address;     // IPv4地址类型

// 以太网头部
header EthernetHdr {
    MacAddress dstAddr;
    MacAddress srcAddr;
    bit<16>    type;
}

// 自定义隧道头部
header TunnelHdr {
    bit<16> protoId;    // 协议ID
    bit<16> destId;     // 目的隧道ID
}

// IPv4头部
header Ipv4Hdr {
    bit<4>    version;
    bit<4>    ihl;
    bit<8>    diffserv;
    bit<16>   totalLen;
    bit<16>   identification;
    bit<3>    flags;
    bit<13>   fragOffset;
    bit<8>    ttl;
    bit<8>    protocol;
    bit<16>   hdrChecksum;
    Ip4Address srcAddr;
    Ip4Address dstAddr;
}

// ARP头部
header ArpHdr {
    bit<16> htype;      // 硬件地址类型
    bit<16> ptype;      // 协议地址类型
    bit<8>  hlen;       // 硬件地址长度
    bit<8>  plen;       // 协议地址长度
    bit<16> oper;       // 操作类型（请求/回复）
    MacAddress sha;     // 发送端MAC
    Ip4Address spa;     // 发送端IP
    MacAddress tha;     // 目标端MAC
    Ip4Address tpa;     // 目标端IP
}

// 元数据结构
struct Metadata {
    Ip4Address targetIp;    // 目标IP地址
}

// 头部集合
struct Headers {
    EthernetHdr eth;
    ArpHdr      arp;
    TunnelHdr   tunnel;
    Ipv4Hdr     ipv4;
}

/*************************************************************************
************************** 解析器定义 ***********************************
*************************************************************************/

parser PacketParser(
    packet_in packet,
    out Headers hdr,
    inout Metadata meta,
    inout standard_metadata_t stdMeta
) {
    state start {
        transition parseEthernet;
    }

    state parseEthernet {
        packet.extract(hdr.eth);
        transition select(hdr.eth.type) {
            TUNNEL_TYPE: parseTunnel;
            IPV4_TYPE:   parseIpv4;
            ARP_TYPE:    parseArp;
            default:     accept;
        }
    }

    state parseArp {
        packet.extract(hdr.arp);
        meta.targetIp = hdr.arp.tpa;  // 保存目标IP
        transition accept;
    }

    state parseTunnel {
        packet.extract(hdr.tunnel);
        transition select(hdr.tunnel.protoId) {
            IPV4_TYPE: parseIpv4;
            default:   accept;
        }
    }

    state parseIpv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }
}

/*************************************************************************
********************* 校验和验证器 *************************************
*************************************************************************/

control ChecksumVerifier(
    inout Headers hdr,
    inout Metadata meta
) {
    apply { }
}

/*************************************************************************
********************* 入口处理逻辑 *************************************
*************************************************************************/

control IngressProcessor(
    inout Headers hdr,
    inout Metadata meta,
    inout standard_metadata_t stdMeta
) {
    // 隧道计数器定义
    counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) ingressCounter;
    counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) egressCounter;

    // 基本动作：丢弃数据包
    action dropPacket() {
        mark_to_drop(stdMeta);
    }

    // IPv4转发动作
    action forwardIpv4(MacAddress dstMac, PortId outPort) {
        stdMeta.egress_spec = outPort;
        hdr.eth.srcAddr = hdr.eth.dstAddr;
        hdr.eth.dstAddr = dstMac;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

    // 隧道封装动作
    action encapsulateTunnel(bit<16> tunnelId) {
        hdr.tunnel.setValid();
        hdr.tunnel.destId = tunnelId;
        hdr.tunnel.protoId = hdr.eth.type;
        hdr.eth.type = TUNNEL_TYPE;
        ingressCounter.count((bit<32>)hdr.tunnel.destId);
    }

    // IPv4查找表（最长前缀匹配）
    table ipv4RouteTable {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            forwardIpv4;
            encapsulateTunnel;
            dropPacket;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

    // 隧道转发动作
    action forwardTunnel(PortId outPort) {
        stdMeta.egress_spec = outPort;
    }

    // 隧道解封装动作
    action decapsulateTunnel(MacAddress dstMac, PortId outPort) {
        stdMeta.egress_spec = outPort;
        hdr.eth.dstAddr = dstMac;
        hdr.eth.type = hdr.tunnel.protoId;
        egressCounter.count((bit<32>)hdr.tunnel.destId);
        hdr.tunnel.setInvalid();
    }

    // 隧道查找表（精确匹配）
    table tunnelRouteTable {
        key = {
            hdr.tunnel.destId: exact;
        }
        actions = {
            forwardTunnel;
            decapsulateTunnel;
            dropPacket;
        }
        size = 1024;
        default_action = dropPacket();
    }

    // ARP回复动作
    action replyArp(MacAddress localMac) {
        hdr.eth.dstAddr = hdr.arp.sha;
        hdr.eth.srcAddr = localMac;

        hdr.arp.oper = ARP_REPLY;
        hdr.arp.tha = hdr.arp.sha;
        hdr.arp.tpa = hdr.arp.spa;
        hdr.arp.sha = localMac;
        hdr.arp.spa = meta.targetIp;

        stdMeta.egress_spec = stdMeta.ingress_port;
    }

    // ARP查找表
    table arpLookupTable {
        key = {
            hdr.arp.oper: exact;
            hdr.arp.tpa:  lpm;
        }
        actions = {
            replyArp;
            dropPacket;
        }
        const default_action = dropPacket();
    }

    // 入口处理主逻辑
    apply {
        if (hdr.eth.type == ARP_TYPE) {
            arpLookupTable.apply();
        } else {
            if (hdr.ipv4.isValid() && !hdr.tunnel.isValid()) {
                ipv4RouteTable.apply();
            }

            if (hdr.tunnel.isValid()) {
                tunnelRouteTable.apply();
            }
        }
    }
}

/*************************************************************************
********************* 出口处理逻辑 *************************************
*************************************************************************/

control EgressProcessor(
    inout Headers hdr,
    inout Metadata meta,
    inout standard_metadata_t stdMeta
) {
    apply { }
}

/*************************************************************************
********************* 校验和计算器 *************************************
*************************************************************************/

control ChecksumCalculator(
    inout Headers hdr,
    inout Metadata meta
) {
    apply {
        update_checksum(
            hdr.ipv4.isValid(),
            {
                hdr.ipv4.version,
                hdr.ipv4.ihl,
                hdr.ipv4.diffserv,
                hdr.ipv4.totalLen,
                hdr.ipv4.identification,
                hdr.ipv4.flags,
                hdr.ipv4.fragOffset,
                hdr.ipv4.ttl,
                hdr.ipv4.protocol,
                hdr.ipv4.srcAddr,
                hdr.ipv4.dstAddr
            },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16
        );
    }
}

/*************************************************************************
********************* 解解析器 *****************************************
*************************************************************************/

control PacketDeparser(
    packet_out packet,
    in Headers hdr
) {
    apply {
        packet.emit(hdr.eth);
        packet.emit(hdr.arp);
        packet.emit(hdr.tunnel);
        packet.emit(hdr.ipv4);
    }
}

/*************************************************************************
********************* 交换机整体定义 ***********************************
*************************************************************************/

V1Switch(
    PacketParser(),
    ChecksumVerifier(),
    IngressProcessor(),
    EgressProcessor(),
    ChecksumCalculator(),
    PacketDeparser()
) main;